using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Data.Common;
using StackExchange.Profiling;
using StackExchange.Profiling.Data;

#if DEBUG
#else
using System.Threading.Tasks;
#endif

#if DEBUG

#endif

namespace Smarly.Core.Database
{


	public static class SqlRepository 
	{
		class ExecuteAsyncState
		{
			public object Obj { get; set; }
		}

		static SqlRepository()
		{
			const string connectionName = "SmarlyConnection";
			ConnectionStringSettings conString = ConfigurationManager.ConnectionStrings[connectionName];

			if (conString == null || String.IsNullOrWhiteSpace(conString.ConnectionString))
				throw new ApplicationException(String.Format("Couldn't find Connection string: '{0}'", connectionName));

			ConnectionString = new SqlConnectionStringBuilder(conString.ConnectionString)
			{
				AsynchronousProcessing = true
			}.ToString();
		}


		private static string ConnectionString { get; set; }

#if DEBUG
		public static ProfiledDbConnection GetSqlConnection
		{
			get
			{
				var cnn = new SqlConnection(ConnectionString);
				var profiledDbConnection = new ProfiledDbConnection(cnn, MiniProfiler.Current);
				return profiledDbConnection;
			}
		}
#endif

		public static T ExecuteProcedureReader<T>(string spName, List<SpParam> spParams, SqlInfoMessageEventHandler sqlInfoMessageHandler, ReadHandler<T> parse) where T : class
		{
#if DEBUG
			using (var connection = GetSqlConnection)
			{
				using (DbCommand command = connection.CreateCommand())
#else
			using (var connection = new SqlConnection(ConnectionString))
			{
				using (SqlCommand command = new SqlCommand()
				                            	{
				                            		CommandType = CommandType.StoredProcedure,
				                            		Connection = connection,
				                            		CommandText = spName,
				                            	})
#endif

				{

					command.CommandType = CommandType.StoredProcedure;
					command.CommandText = spName;


#if DEBUG
					if (sqlInfoMessageHandler != null)
						((SqlConnection)connection.WrappedConnection).InfoMessage += sqlInfoMessageHandler;
#else
					if (sqlInfoMessageHandler != null)
						connection.InfoMessage += sqlInfoMessageHandler;
#endif

					if (spParams != null)
						command.Parameters.AddRange(spParams.Select(x => new SqlParameter(x.Name, x.Value)).ToArray());

					try
					{
						connection.Open();

						T result = null;

#if DEBUG
						using (var reader = command.ExecuteReader())
#else
						using (var reader = Task<SqlDataReader>
							.Factory
							.FromAsync(command.BeginExecuteReader, command.EndExecuteReader, new ExecuteAsyncState { Obj = "preved" },
												 TaskCreationOptions.None)
							.Result)
#endif
						{
							result = parse(reader);
						}
						return result;
					}
					catch (Exception e)
					{
						string message = e.InnerException != null ? e.InnerException.Message : e.Message;
						switch (message)
						{
							case "UnknownUrl":
								throw new UnknownPageException();
							default:
								throw;
						}
					}
				}
			}
		}

	}

	public delegate T ReadHandler<T>(IDataReader reader);

	public struct SpParam
	{
		public string Name;
		public DbType Type;
		public Object Value;
	}

}